import d4dset1
from datetime import datetime, timedelta

ant_subs = [
    [196,909,1000,739,1030,425,744,542,892],
    [279,994,40,124,394,742,908],
    [292,746,307,344,143,738,821,245,509,839,1231,735,998]]

all_ant = ant_subs[0] + ant_subs[1] + ant_subs[2]

def minute(duration):
    return duration.seconds/60

def firstN_with_topFreq(monitor_start_datetime, assign_duration, max_delay,
        sense_duration, ant_sub, firstn, topn_freq, n_hoped):
    start_hour = monitor_start_datetime.hour
    end_hour = (monitor_start_datetime+assign_duration+max_delay).hour
    #candidates = get_candidates_by_frequency(ant_sub, topn_freq)
    candidates = get_candidates_by_frequency(ant_sub, topn_freq, start_hour, end_hour)
    return firstN(monitor_start_datetime, assign_duration, max_delay,
            sense_duration, ant_sub, firstn, n_hoped, candidates)

def flooding(monitor_start_datetime, assign_duration, max_delay, sense_duration,
        ant_sub, n_hoped=-1):
    return firstN(monitor_start_datetime, assign_duration, max_delay, 
            sense_duration, ant_sub, -1, n_hoped) 

def firstN(monitor_start_datetime, assign_duration, max_delay, 
        sense_duration, ant_sub, firstn, n_hoped, candidates=None):
    """
    baseline algorithm flooding or FirstN. 
    if set firstn > 0, then will use FirstN methods, otherwise flooding.
    """
    assigned = [0] * 500
    received = [0] * 500
    
    monitor_end_datetime = monitor_start_datetime + assign_duration
    callers = d4dset1.select_caller_with_first_call_time(
            ant_sub, monitor_start_datetime, monitor_end_datetime)
    
    n = 0
    for caller in callers:
        callerid = caller[0]
        calltime = caller[1]
        if candidates is None or candidates.count(callerid)>0: 
        #candidates should cover the caller to assign
            time_diff_minu = (calltime - monitor_start_datetime).seconds/60
            r = 0
            if time_diff_minu>=1:
                for i in xrange(0, time_diff_minu):
                    r += received[i]
            if n_hoped>0 and r>=n_hoped:
                break

            assigned[time_diff_minu] += 1
            n+=1
            next_call_time = d4dset1.query_first_call_time(
                    callerid, ant_sub, calltime+sense_duration, monitor_end_datetime+max_delay)
            if next_call_time is not None:
                time_diff_minu = (next_call_time - monitor_start_datetime).seconds/60
                received[time_diff_minu] += 1

            if n==firstn: #for flooding firstn=-1, so this will always be false
                break

    metric_delay = minute(max_delay)

    a_cum = r_cum = e_cum = 0
    for i in xrange(0, (assign_duration+max_delay).seconds/60+1):
        a_cum += assigned[i]
        r_cum += received[i]
        if n_hoped > 0 and r_cum >= n_hoped: #got enough results, no need more assignment
            tmp_delay = i - minute(assign_duration)
            metric_delay = min(tmp_delay, metric_delay)
        #print "{}\t{}\t{}\t{}".format(i, a_cum, r_cum, metric_delay)
    
    #print monitor_start_datetime, minute(assign_duration),
    #print minute(max_delay), minute(sense_duration), a_cum, r_cum
    #printResult(monitor_start_datetime, assign_duration, max_delay, sense_duration,
            #firstn, n_hoped, a_cum, r_cum, metric_delay)
    
    print minute(max_delay), n_hoped, r_cum, a_cum, metric_delay if metric_delay>0 else 0

def printResult(monitor_start_datetime, assign_duration, max_delay, sense_duration,
            firstn, n_hoped, a_cum, r_cum, metric_delay):
    if firstn <= 0:
        print "------flooding------"
    else:
        print "------firstN N={}------".format(firstn)
    print 'start time:', monitor_start_datetime
    print 'assign duration:', minute(assign_duration), 'min'
    print 'max delay:', minute(max_delay), 'min'
    print 'sense_duration:', minute(sense_duration), 'min'
    print '#assigned:', a_cum
    print '#received:', r_cum

    if (n_hoped > 0):
        n_valid = min(r_cum, n_hoped)
        print "#expected:", n_hoped
        print "#valid:", n_valid
        print "coverage:", n_valid*1.0/n_hoped
        print "delay:", metric_delay
        print "energy efficency:", n_valid*1.0/a_cum

def get_candidates_by_frequency(ant_sub, topn, start_hour=0, end_hour=23):
    topn_freq_callers = d4dset1.select_top_frequency_caller(ant_sub, topn, start_hour, end_hour)
    candidates = []
    for caller in topn_freq_callers:
        candidates.append(caller[0])
    return candidates

def sense_duration_analysis(start_time, assign_duration, max_delay, ant_sub):
    for i in range(1,31):
        flooding(start_time, assign_duration, max_delay, timedelta(minutes=i), ant_sub)

def sense_duration_analysis_exp():
    start_time = datetime(2011, 12, 7, 10)
    assign_dur = timedelta(minutes=30)
    max_delay = timedelta(minutes=180)
    ant_sub = ant_subs[0]
    sense_duration_analysis(start_time, assign_dur, max_delay, ant_sub)

def start_time_analysis(
            start_time0, increase_step, increase_count, 
            assign_duration, max_delay, sense_duration, ant_sub):
    for i in range(increase_count):
        start_time = start_time0 + i * increase_step
        flooding(start_time, assign_duration, max_delay, sense_duration, ant_sub)

def start_time_analysis_exp():
    assign_dur = timedelta(minutes=60)
    max_delay = timedelta(minutes=60)
    sense_duration = timedelta(minutes=5)
    ant_sub = ant_subs[0]

    start_time0 = datetime(2011, 12, 8, 0, 0, 0)
    increase_step = timedelta(minutes=30)
    increase_count = 48 
    
    start_time_analysis(start_time0, increase_step, increase_count,
            assign_dur, max_delay, sense_duration, ant_sub)

def max_delay_analysis(start_time, assign_duration, sense_duration, ant_sub, firstn, nhoped, topfreq=-1):
    for i in xrange(5, 61, 5):
        if topfreq <= 0:
            firstN(start_time, assign_duration, 
                timedelta(minutes=i), sense_duration, ant_sub, firstn, nhoped)
        else:
            firstN_with_topFreq(start_time, assign_duration, 
                timedelta(minutes=i), sense_duration, ant_sub, firstn, topfreq, nhoped)

def max_delay_analysis_exp(firstN, topfreq=-1):
    start_time = datetime(2011, 12, 7, 10)
    assign_dur = timedelta(minutes=30)
    sense_duration = timedelta(minutes=5)
    nhoped = 20
    ant_sub = ant_subs[2] 
    max_delay_analysis(start_time, assign_dur, sense_duration, ant_sub, firstN * nhoped, nhoped, topfreq)

def assign_duration_analysis(start_time, max_delay, sense_duration, ant_sub):
    for i in xrange(10, 300):
        assign_duration = timedelta(minutes=i)
        flooding(start_time, assign_duration, max_delay, sense_duration, ant_sub)

def assign_duration_analysis_exp():
    start_time = datetime(2011, 12, 7, 12)
    max_delay = timedelta(minutes=60)
    sense_duration = timedelta(minutes=5)
    ant_sub = ant_subs[2]
    assign_duration_analysis(start_time, max_delay, sense_duration, ant_sub)

def run_one_set():
    start_time = datetime(2011, 12, 7, 9)
    assign_duration = timedelta(minutes=30)
    max_delay = timedelta(minutes=30)
    sense_duration = timedelta(minutes=5)
    ant_sub = ant_subs[2]
    n_hoped = 25 
    
    flooding(start_time, assign_duration, max_delay, sense_duration, 
            ant_sub, n_hoped)
    for i in (2, 1.9, 1.8, 1.7, 1.6, 1.5, 1.4, 1.3, 1.2, 1.1, 1.0):
        firstN(start_time, assig_duration, max_delay, sense_duration, 
                ant_sub, (int)(n_hoped*i), n_hoped)

def n_hopes_analysis_exp(firstn_mult, topn_freq=-1, topn_freq_mult=-1):
    start_time = datetime(2011, 12, 7, 9)
    assign_duration = timedelta(minutes=30)
    max_delay = timedelta(minutes=60)
    sense_duration = timedelta(minutes=5)
    ant_sub = ant_subs[2]
    for n in range(5, 41, 2):
        if (topn_freq<=0 and topn_freq_mult<=0):
            firstN(start_time, assign_duration, max_delay, sense_duration, 
                    ant_sub, (int)(n*firstn_mult), n)
        elif topn_freq>0:
            firstN_with_topFreq(start_time, assign_duration, max_delay, sense_duration,
                    ant_sub, (int)(n*firstn_mult), topn_freq, n)
        else:
            firstN_with_topFreq(start_time, assign_duration, max_delay, sense_duration,
                    ant_sub, (int)(n*firstn_mult), n * topn_freq_mult, n)

def firstn_with_topfreq_diff_topn_exp():
    start_time = datetime(2011, 12, 7, 12)
    assign_duration = timedelta(minutes=30)
    max_delay = timedelta(minutes=30)
    sense_duration = timedelta(minutes=5)
    ant_sub = ant_subs[2]
    n_hoped = 40 
    firstn = 1.5*n_hoped
    print "flooding"
    flooding(start_time, assign_duration, max_delay, sense_duration,
            ant_sub, n_hoped)
    print "firstn"
    firstN(start_time, assign_duration, max_delay, sense_duration,
            ant_sub, firstn, n_hoped)
    print "firstn with topfreq"
    for topn in range(100, 1001, 50):
        print topn,
        firstN_with_topFreq(start_time, assign_duration, max_delay, sense_duration,
                ant_sub, firstn, topn, n_hoped)


if __name__ == '__main__':
    #sense_duration_analysis_exp()
    #start_time_analysis_exp()
    max_delay_analysis_exp(-1, -1)
    #assign_duration_analysis_exp()
    #run_one_set()
    #n_hopes_analysis_exp(1.6)
    #firstn_with_topfreq_diff_topn_exp()

    #flooding(datetime(2011, 12, 7, 10), timedelta(minutes=30), timedelta(minutes=30),
            #timedelta(minutes=5), ant_subs[2], 10)

    #firstN(datetime(2011, 12, 7, 10), timedelta(minutes=30), timedelta(minutes=30),
            #timedelta(minutes=5), ant_subs[2], 60, 30)


